//*************************************************************************************
// File:     SimulaTimer.cs
//*************************************************************************************
// Description: Encapsula un temporizador sencillo.
//*************************************************************************************
// Classes:      SimulaTimer
//*************************************************************************************
// Author:      ManOguaR
//*************************************************************************************

using Xeralabs.Core.Utils.Internals.Platform;

namespace Xeralabs.Core.Utils.Timing
{
    /// <summary>
    /// Clase interna que representa un temporizador.
    /// </summary>
    public class SimulaTimer
    {
        #region Variables Locales
        protected bool _isUsingQPF;
        protected bool _isTimerStopped;
        protected long _ticksPerSecond;
        protected long _stopTime;
        protected long _lastElapsedTime;
        protected long _baseTime;
        #endregion

        #region Propiedades
        /// <summary>
        /// Obtiene True si el temporizador esta detenido.
        /// </summary>
        public bool IsStopped { get { return _isTimerStopped; } }
        #endregion

        /// <summary>
        /// Constructor.
        /// </summary>
        public SimulaTimer()
        {
            _isTimerStopped = true;
            _ticksPerSecond = 0;
            _stopTime = 0;
            _lastElapsedTime = 0;
            _baseTime = 0;
            //Usar QueryPerformanceFrequency para obtener la frecuencia del temporizador
#if x64
            _isUsingQPF = Win64DllImporter.QueryPerformanceFrequency(ref _ticksPerSecond);
#else
            _isUsingQPF = Win32DllImporter.QueryPerformanceFrequency(ref _ticksPerSecond);
#endif
        }

        /// <summary>
        /// Metodo para resetear el temporizador a 0 sin alterar si estado actual.
        /// </summary>
        public void Reset()
        {
            if (!_isUsingQPF) return; //Nada que hacer

            //Obtener el tiempo actual o el de parada
            long time = 0;

            if (_stopTime != 0) time = _stopTime;
            else
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif

            _baseTime = time;
            _lastElapsedTime = time;
            _stopTime = 0;
            _isTimerStopped = false;
        }
        /// <summary>
        /// Inicia el temporizador.
        /// </summary>
        public void Start()
        {
            if (!_isUsingQPF) return; //Nada que hacer

            //Obtener el tiempo actual o el de parada
            long time = 0;

            if (_stopTime != 0) time = _stopTime;
            else 
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif


            if (_isTimerStopped) _baseTime += (time - _stopTime);
            _stopTime = 0;
            _lastElapsedTime = time;
            _isTimerStopped = false;
        }
        /// <summary>
        /// Metodo para detener (or pausar) el temporizador.
        /// </summary>
        public void Stop()
        {
            if (!_isUsingQPF)
                return; //Nada que hacer

            if (!_isTimerStopped)
            {
                //Obtener el tiempo actual o el de parada
                long time = 0;
                if (_stopTime != 0)
                    time = _stopTime;
                else
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif

                _stopTime = time;
                _lastElapsedTime = time;
                _isTimerStopped = true;
            }
        }

        /// <summary>
        /// Metodo para avanzar el timer 1/10 de segundo.
        /// </summary>
        public void Advance()
        {
            if (!_isUsingQPF) return; //Nada que hacer

            _stopTime += _ticksPerSecond / 10;
        }

        /// <summary>
        /// Metodo para obtener el tiempo absoluto desde que se inicio el temporizador.
        /// </summary>
        /// <returns>
        /// El tiempo transcurrido desde que se inicio el sistema, en segundos.
        /// </returns>
        public double GetAbsoluteTime()
        {
            if (!_isUsingQPF) return -1.0; //Nada que hacer

            //Obtener el tiempo actual o el de parada
            long time = 0;

            if (_stopTime != 0) time = _stopTime;
            else 
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif

            double absolueTime = time / (double)_ticksPerSecond;
            return absolueTime;
        }
        /// <summary>
        /// Metodo para obtener el tiempo actual modificado segun paradas y avances.
        /// </summary>
        /// <returns>
        /// El tiempo actual del temporizador, en segundos.
        /// </returns>
        public double GetTime()
        {
            if (!_isUsingQPF) return -1.0; //Nada que hacer

            //Obtener el tiempo actual o el de parada
            long time = 0;
            if (_stopTime != 0) time = _stopTime;
            else 
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif

            double appTime = (double)(time - _baseTime) / (double)_ticksPerSecond;
            return appTime;
        }
        /// <summary>
        /// Metodo para obtener el tiempo transcurrido entre dos llamadas GetElapsedTime(), modificado segun paradas y
        /// avances.
        /// </summary>
        /// <returns>
        /// El tiempo transcurrido desde la ultima llamada GetElapsedTime(), en segundos.
        /// </returns>
        public double GetElapsedTime()
        {
            if (!_isUsingQPF) return -1.0; //Nada que hacer

            //Obtener el tiempo actual o el de parada
            long time = 0;
            if (_stopTime != 0) time = _stopTime;
            else
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif

            double elapsedTime = (double)(time - _lastElapsedTime) / (double)_ticksPerSecond;
            _lastElapsedTime = time;
            
            return elapsedTime;
        }
    }
}
